<?php
namespace App\Http\Middleware;

use App\Exception\LimitedException;
use App\Http\Lib\Service;
use App\Http\Lib\ServiceInfo;
use App\Http\Limter\RateLimiter;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\BeanFactory;
use Swoft\Console\Router\Module;
use Swoft\Http\Message\Request;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Annotation\Mapping\Middleware;
use Swoft\Http\Server\Contract\MiddlewareInterface;

/**
 * Class LimiterMiddleware
 * @package App\Http\Middleware
 * @Bean("LimiterMiddleware")
 */
class LimiterMiddleware implements MiddlewareInterface
{

    /**
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     * @throws LimitedException
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 如果开启了限流
        if (! constant(Module::DEFINE_DISABLE_LIMITER_MODULE)) {
            // 获取上级中间件中传递的服务信息
            /** @var Service $service */
            $service = $request->getAttribute('service');
            /** @var ServiceInfo $serviceInfo */
            $serviceInfo = $request->getAttribute('serviceInfo');

            // 判断 请求的 action 是否需要校验
            if($service->needLimit()) {
                /** @var RateLimiter $Limiter */
                $Limiter = BeanFactory::getBean("Limiter");
                return $Limiter->checkRate(
                    $serviceInfo->getServer(),
                    $serviceInfo->getAction(),
                    [
                        'key'     => $service->getId(),
                        'rate'    => $service->getLimiterRate(),
                        'max'     => $service->getLimiterMax(),
                        'fallback'=> function(){
                            throw new LimitedException();
                        }
                    ],
                    function() use($handler, $request){
                        return $handler->handle($request);
                    }
                );
            }
        }
        return $handler->handle($request);
    }
}
